<?php
/**
 * Created by PhpStorm.
 * User: DongYao
 * Date: 2017/12/21
 * Time: 下午7:57
 */

namespace App\Support\Order;

use App\Support\Facades\OperationLog;
use App\Tables\ChargeOrder as Order;
use App\Tables\UserBalanceTransferRecord;
use App\Tables\User;
use App\Tables\UserScoreTransferRecord;
use Carbon\Carbon;
use EasyWeChat;
use Exception;
use Throwable;
use DB;
use Log;
use Illuminate\Database\QueryException;
use AlipayTradeWapPayRequest;
use AlipayTradeAppPayRequest;
use XmtApp\Payment\Alipay\Alipay;
use XmtApp\Payment\Alipay\HandleNotify;

class ChargeOrder
{
    /**
     * 生成充值订单
     * @param $user_id
     * @param $money
     * @param $pay_mode
     * @param string $source
     * @return Order
     */
    public function create($user_id, $money, $pay_mode, $source = 'wap')
    {
        $order = new Order();
        $order->user_id = $user_id;
        $order->order_sn = $order->makeOrderSn();
        $order->money = $money;
        $order->status = 'wait_pay';
        $order->pay_mode = $pay_mode;
        $order->source = $source;
        $order->give_score = find_charge_give_score($money);
        $order->save();

        return $order;
    }

    /**
     * @param $order_sn
     * @param $open_id
     * @param $notify_url
     * @param string $source
     * @param string $body
     * @return array|bool|string
     * @internal param string $trade_type
     */
    public function wxpay($order_sn, $open_id, $notify_url,  $source = 'wap', $body = '账户余额充值')
    {
        DB::beginTransaction();
        try {
            $order = Order::where('order_sn', $order_sn)->first();

            if (empty($order)) {
                throw new Exception("充值订单{$order_sn}不存在");
            }

            $order->pay_mode = 'wxpay';
            $order->source = $source;
            $order->save();

            if ($source == 'wap') {
                $app = EasyWeChat::payment('default');
                $result = $app->order->unify([
                    'body' => $body,
                    'out_trade_no' => $order_sn,
                    'total_fee' => bcmul($order->money, 100),
                    'notify_url' => $notify_url, // 支付结果通知网址，如果不设置则会使用配置里的默认地址
                    'trade_type' => 'JSAPI',
                    'openid' => $open_id,
                ]);

                Log::debug('Wap微信支付订单接口返回参数', [$result]);

                if (!isset($result->prepay_id)) {
                    throw new Exception("充值订单{$order_sn}生成微信支付单失败，获取prepay_id失败 【{$result->toJson()}】");
                }
                $pay_config = $app->jssdk->bridgeConfig($result->prepay_id, false);
            } else if (in_array($source, ['ios', 'android'])) {
                $app = EasyWeChat::payment('open_platform');
                $result = $app->order->unify([
                    'body' => $body,
                    'out_trade_no' => $order_sn,
                    'total_fee' => bcmul($order->money, 100),
                    'notify_url' => $notify_url,
                    'trade_type' => 'APP',
                ]);

                Log::debug('App微信支付订单接口返回参数', [$result]);

                $pay_config = $app->jssdk->appConfig($result->prepay_id);
            } else {
                throw new Exception("充值订单{$order_sn}生成微信支付单失败，未知支付来源{$source}");
            }

            DB::commit();

            /**
             * 返回支付配置参数
             */
            return $pay_config;
        } catch (Exception $exception) {
            Log::debug('微信充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Throwable $exception) {
            Log::debug('微信充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        }

        return false;
    }

    /**
     * 支付宝支付
     * @param $order_sn
     * @param string $detail
     * @param string $source
     * @return bool
     */
    public function alipay($order_sn, $source = 'wap',  $detail = '账户余额充值')
    {
        DB::beginTransaction();
        try {
            $order = Order::where('order_sn', $order_sn)->first();

            if (empty($order)) {
                throw new Exception("充值订单{$order_sn}不存在");
            }

            $order->pay_mode = 'alipay';
            $order->source = $source;
            $order->save();

            //支付宝支付
            $return_url = route('order.charge.alipay.return');
            $notify_url = route('order.charge.alipay.notify');

            // 支付链接
            $pay_url = '';
            if ($source == 'wap') {
                $wap_request = new AlipayTradeWapPayRequest ();
                $wap_request->setReturnUrl($return_url);
                $wap_request->setNotifyUrl($notify_url);

                $pay_data = [
                    'subject' => $detail,
                    'out_trade_no' => $order->order_sn,
                    'timeout_express' => '15d',
                    'total_amount' => $order->money,
                    'product_code' => 'QUICK_WAP_WAY'
                ];
                $wap_request->setBizContent(json_encode($pay_data, JSON_UNESCAPED_UNICODE));

                $alipay = resolve(Alipay::class);
                $pay_url = $alipay->pageExecute($wap_request, 'GET');
                if (empty($pay_url)) {
                    throw new Exception("充值订单{$order_sn}生成支付宝支付单失败, 支付来源{$source}");
                }
                Log::debug('支付宝充值链接-WAP', [$pay_url]);
            } else if (in_array($source, ['ios', 'android'])) {
                $app_request = new AlipayTradeAppPayRequest ();
                $app_request->setNotifyUrl($notify_url);
                $pay_data = [
                    'subject' => $detail,
                    'out_trade_no' => $order->order_sn,
                    'timeout_express' => '15d',
                    'total_amount' => $order->money,
                    'product_code' => 'QUICK_MSECURITY_PAY'
                ];
                $app_request->setNeedEncrypt(true);
                $app_request->setBizContent(json_encode($pay_data, JSON_UNESCAPED_UNICODE));

                $alipay = resolve(Alipay::class);
                $pay_url = $alipay->sdkExecute($app_request);
                if (empty($pay_url)) {
                    throw new Exception("充值订单{$order_sn}生成支付宝支付单失败, 支付来源{$source}");
                }
                // $pay_url = htmlspecialchars($pay_url);
                Log::debug('支付宝充值链接-APP', [$pay_url]);
            } else {
                throw new Exception("充值订单{$order_sn}生成支付宝支付单失败, 未知支付来源{$source}");
            }

            DB::commit();

            return $pay_url;
        } catch (QueryException $exception) {
            Log::debug('支付宝充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Exception $exception) {
            Log::debug('支付宝充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Throwable $exception) {
            Log::debug('支付宝充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        }

        return false;
    }

    /**
     * 处理微信支付回调
     */
    public function handleNotify()
    {
        $app = EasyWeChat::payment('default');

        $self = $this;
        $response = $app->handlePaidNotify(function($message, $fail) use ($self) {
            $payment_sn = $message['out_trade_no']; // 平台自己的支付订单号
            $trade_sn = $message['transaction_id']; //支付宝交易单号
            $successful = $message['result_code'] === 'SUCCESS';

            Log::debug('Wap微信充值异步回调参数', [$message, $successful]);

            $res =  $self->paySuccessCallback($payment_sn, $trade_sn, $successful);

            if ($res === false) {
                $fail('订单充值失败');
            }
            return $res;
        });

        $response->send(); // return $response;
    }

    /**
     * 处理App微信支付回调
     */
    public function handleAppNotify()
    {
        $app = EasyWeChat::payment('open_platform');

        $self = $this;
        $response = $app->handlePaidNotify(function($message, $fail) use ($self) {
            $payment_sn = $message['out_trade_no']; // 平台自己的支付订单号
            $trade_sn = $message['transaction_id']; //支付宝交易单号
            $successful = $message['result_code'] === 'SUCCESS';

            Log::debug('App微信充值异步回调参数', [$message, $successful]);

            $res =  $self->paySuccessCallback($payment_sn, $trade_sn, $successful);

            if ($res === false) {
                $fail('订单充值失败');
            }
            return $res;
        });

        $response->send(); // return $response;
    }

    /**
     * 处理支付宝同步回调
     * @param HandleNotify $notify
     * @return \Illuminate\Http\RedirectResponse
     */
    public function handleAlipayReturn(HandleNotify $notify)
    {
        $back_url = route('webapp.home') . "#/order/charge/list";

        $notify->handleReturn(function ($notify, $successful) {
            Log::debug('支付宝充值同步回调参数', [$notify->all(), $successful]);
        });

        return redirect()->to($back_url);
    }

    /**
     * 处理支付宝异步回调
     * @param HandleNotify $notify
     * @return mixed
     */
    public function handleAlipayNotify(HandleNotify $notify)
    {
        $response = $notify->handleNotify(function ($notify, $successful) {
            $payment_sn = $notify->out_trade_no; // 平台自己的支付订单号
            $trade_sn = $notify->trade_no; //支付宝交易单号

            Log::debug('支付宝充值异步回调参数', [$notify->all(), $successful]);

            return self::paySuccessCallback($payment_sn, $trade_sn, $successful);
        });

        return $response;
    }

    /**
     * 处理充值订单支付回调
     * @param $payment_sn
     * @param $trade_sn
     * @param $successful
     * @return bool
     */
    public function paySuccessCallback($payment_sn, $trade_sn, $successful)
    {
        DB::beginTransaction();
        try {
            // 查找支付订单
            $order = Order::where('order_sn', $payment_sn)->first();

            if (!$order) { // 如果订单不存在
                throw new Exception('订单不存在');
            }

            // 检查订单是否已经更新过支付状态
            if ($order->paid_at) { // 假设订单字段“支付时间”不为空代表已经支付
                return true; // 已经支付成功了就不再更新了
            }

            // 用户是否支付成功
            if ($successful) {
                // 不是已经支付状态则修改为已经支付状态
                $order->paid_at = Carbon::now(); // 更新支付时间为当前时间
                $order->status = 'paid';
                $order->out_trade_sn = $trade_sn;
                $res = $order->save(); // 保存订单
                if ($res === false) {
                    throw new Exception('订单信息更新失败');
                }

                /**
                 * 更新会员账户余额
                 */
                $user = User::where('id', $order->user_id)->lockForUpdate()->first();
                if (empty($user)) {
                    throw new Exception('下单用户信息不存在');
                }

                // 增加充值赠送积分数量
                if ($order->give_score > 0) {
                    $user->score = bcadd($user->score, $order->give_score, 0);
                }

                $user->balance = bcadd($user->balance, $order->money, 2);
                $res = $user->save();
                if ($res === false) {
                    throw new Exception('更新会员账户余额失败');
                }

                /**
                 * 增加充值赠送积分数量记录
                 */
                if ($order->give_score > 0) {
                    $user_score_record = new UserScoreTransferRecord();
                    $user_score_record->user_id = $order->user_id;
                    $user_score_record->order_sn = $order->order_sn;
                    $user_score_record->type = 'charge_give';
                    $user_score_record->score = $order->give_score;
                    $user_score_record->total_score = $user->score;
                    $res = $user_score_record->save();
                    if ($res === false) {
                        throw new Exception('增加充值赠送积分记录');
                    }
                }

                /**
                 * 增加账户余额变动记录
                 */
                $user_balance_record = new UserBalanceTransferRecord();
                $user_balance_record->user_id = $order->user_id;
                $user_balance_record->order_sn = $order->order_sn;
                $user_balance_record->type = 'charge';
                $user_balance_record->money = $order->money;
                $user_balance_record->balance = $user->balance;
                $res = $user_balance_record->save();
                if ($res === false) {
                    throw new Exception('增加余额账户资金变动记录失败');
                }
            } else { // 用户支付失败
                $order->status = 'paid_fail';
                $res = $order->save(); // 保存订单
                if ($res === false) {
                    throw new Exception('订单信息更新失败');
                }
            }

            DB::commit();
            return true; // 返回处理完成
        } catch (QueryException $exception) {
            Log::debug('充值回调处理失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Exception $exception) {
            Log::debug('充值回调处理失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Throwable $exception) {
            Log::debug('充值回调处理失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        }

        return false;
    }

    /**
     * 后台手动充值
     * @param $user_id
     * @param $money
     * @param $operation_username
     * @return bool
     */
    public function backendCharge($user_id, $money, $operation_username)
    {
        DB::beginTransaction();
        try {
            /**
             * 生成充值订单
             */
            $order = new Order();
            $order->user_id = $user_id;
            $order->order_sn = $order->makeOrderSn();
            $order->money = $money;
            $order->status = 'paid';
            $order->pay_mode = 'backend';
            $order->paid_at = Carbon::now();
            if (!$order->save()) {
                throw new Exception('生成充值订单失败');
            }

            /**
             * 更新会员账户余额
             */
            $user = User::where('id', $order->user_id)->lockForUpdate()->first();
            if (empty($user)) {
                throw new Exception('下单用户信息不存在');
            }

            $user->balance = bcadd($user->balance, $order->money, 2);
            $res = $user->save();
            if ($res === false) {
                throw new Exception('更新会员账户余额失败');
            }

            /**
             * 增加账户余额变动记录
             */
            $user_balance_record = new UserBalanceTransferRecord();
            $user_balance_record->user_id = $order->user_id;
            $user_balance_record->order_sn = $order->order_sn;
            $user_balance_record->type = 'charge';
            $user_balance_record->money = $order->money;
            $user_balance_record->balance = $user->balance;
            $res = $user_balance_record->save();
            if ($res === false) {
                throw new Exception('增加余额账户资金变动记录失败');
            }
            DB::commit();

            // 记录操作日志
            $log_message = "管理员【{$operation_username}】手动给用户【{$user->name}】账户余额充值了{$money}元";
            OperationLog::info('后台手动充值', $log_message);
            return true;
        } catch (QueryException $exception) {
            Log::debug('后台充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Exception $exception) {
            Log::debug('后台充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        } catch (Throwable $exception) {
            Log::debug('后台充值失败', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
            DB::rollBack();
        }
        return false;
    }
}